package com.dhcc.launch;

import com.dhcc.launch.constant.AppConstant;
import com.dhcc.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.*;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目启动器，处理环境变量和配置文件加载
 * @ProjectName Dhcc-Cloud
 * @PackageName com.dhcc
 * @Title DhccApplication
 * @Date 2024/10/1 上午2:00
 * @Author LiuGuoting
 */
@Configuration
public class DhccApplication {
    private static String appName;

    /**
     * 创建应用程序上下文
     * 如需指定参数，可以使用命令行指定参数运行，例如：java -jar app.jar --spring.profiles.active=prod --server.port=8080
     *
     * @param source  启动类
     * @param args    命令行参数
     * @return 配置后的应用上下文
     */
    public static ConfigurableApplicationContext run(Class<?> source, String... args) {
        // 创建并配置SpringApplicationBuilder
        SpringApplicationBuilder builder = createSpringApplicationBuilder(source, args);
        // 返回运行后的应用上下文
        ConfigurableApplicationContext context = builder.run(args);
        // 打印启动信息
        printApplicationInfo(context);
        return context;
    }

    /**
     * 创建SpringApplicationBuilder并配置环境属性
     *
     * @param source  启动类
     * @param args    命令行参数
     * @return 配置后的SpringApplicationBuilder
     */
    private static SpringApplicationBuilder createSpringApplicationBuilder(Class<?> source, String... args) {
        // 读取并配置环境变量
        ConfigurableEnvironment environment = new StandardEnvironment();
        MutablePropertySources propertySources = environment.getPropertySources();
        // 配置属性源（命令行参数、系统属性、环境变量、配置文件）
        configurePropertySources(propertySources, args, environment);
        // 校验应用名称不能为空
        Assert.hasText(appName, "[appName]服务名不能为空");
        // 获取并处理环境变量
        List<String> activeProfiles = Arrays.asList(environment.getActiveProfiles());
        // 获取有效的环境配置
        List<String> profiles = getActiveProfiles(activeProfiles);

        // 配置SpringApplication
        // 解析当前使用的配置文件
        String profile = resolveProfile(profiles);
        // 配置系统属性
        configureSystemProperties(profile);

        // 初始化并返回SpringApplicationBuilder
        return initializeSpringApplicationBuilder(source, appName, profile);
    }

    private static void configurePropertySources(MutablePropertySources propertySources, String[] args, ConfigurableEnvironment environment) {
        // 先检查命令行参数中的 server.port 配置
        boolean isPortConfiguredInArgs = false;
        for (String arg : args) {
            if (arg.startsWith("--server.port=")) {
                isPortConfiguredInArgs =  true;
            }
        }
        if (isPortConfiguredInArgs) {
            // 如果命令行参数中包含 server.port，则优先使用命令行配置
            System.out.println("命令行参数中已配置 server.port，忽略 Nacos 配置中的端口设置。");
        }
        // 添加命令行参数到属性源
        propertySources.addFirst(new SimpleCommandLinePropertySource(args));

        // 添加系统属性到属性源
        propertySources.addLast(new MapPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, environment.getSystemProperties()));

        // 添加系统环境变量到属性源
        propertySources.addLast(new SystemEnvironmentPropertySource(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, environment.getSystemEnvironment()));
        try {
            // 加载应用配置文件
            ResourcePropertySource resourcePropertySource = new ResourcePropertySource(AppConstant.BOOTSTRAP_YML);
            propertySources.addLast(resourcePropertySource);

            // 获取当前激活的环境配置
            String[] activeProfiles = environment.getActiveProfiles();

            if (activeProfiles.length == 0) {
                // 如果没有激活的配置，获取优先级最高的环境配置
                String defaultProfile = getHighestPriorityActiveProfile(propertySources);

                if (defaultProfile != null) {
                    // 设置从属性源中获取的默认环境
                    System.setProperty("spring.profiles.active", defaultProfile);
                } else {
                    // 如果没有找到任何有效配置，使用默认值 dev
                    String fallbackProfile = "dev";
                    System.setProperty("spring.profiles.active", fallbackProfile);
                    System.out.println("无法获取配置文件中的环境，使用默认环境："+ fallbackProfile);
                }
            }else {
                // 获取配置源中的 spring.application.name 属性
                String envstr = resourcePropertySource.getSource().toString();
                Map<String, String> map = mapStringToMap(envstr);
                appName = map.get(AppConstant.APP_NAME);
            }
        } catch (IOException e) {
            // 配置文件加载失败时的错误日志
            System.err.println("单机应用模式，加载Application.yml配置文件！");
        }
        try {
            // 加载应用配置文件
            ResourcePropertySource resourcePropertySource = new ResourcePropertySource(AppConstant.APPLICATION_YML);
            propertySources.addLast(resourcePropertySource);
            // 获取配置源中的 spring.application.name 属性
            String envstr = resourcePropertySource.getSource().toString();
            Map<String, String> map = mapStringToMap(envstr);
            appName = map.get(AppConstant.APP_NAME);
            // 单机应用模式
            System.setProperty("spring.profiles.active", "单机应用模式");
        } catch (IOException e) {
            // 配置文件加载失败时的错误日志
            System.err.println("微服务应用模式，加载Bootstrap.yml配置文件！");
        }
    }

    /**
     * 从属性源中获取优先级最高的 spring.profiles.active 配置
     *
     * @param propertySources 属性源
     * @return spring.profiles.active 的值，或者 null 如果没有找到
     */
    private static String getHighestPriorityActiveProfile(MutablePropertySources propertySources) {
        // 遍历属性源，查找 spring.profiles.active 配置
        for (PropertySource<?> propertySource : propertySources) {
            if (propertySource instanceof ResourcePropertySource) {
                ResourcePropertySource resourcePropertySource = (ResourcePropertySource) propertySource;
                // 获取配置源中的 spring.profiles.active 属性
                String envstr = resourcePropertySource.getSource().toString();
                Map<String, String> map = mapStringToMap(envstr);
                String activeProfile = map.get(AppConstant.ACTIVE);
                appName = map.get(AppConstant.APP_NAME);
                if (activeProfile != null) {
                    // 找到有效的 spring.profiles.active 配置，返回它
                    System.out.println("加载配置文件环境：" + activeProfile);
                    return activeProfile;
                }
            }
        }
        // 如果没有找到 spring.profiles.active 配置，返回 null
        return null;
    }





    /**
     * 获取并处理活动的配置文件
     *
     * @param profiles 当前活动的配置文件列表
     * @return 处理后的配置文件列表
     */
    private static List<String> getActiveProfiles(List<String> profiles) {
        // 预设的环境配置
        List<String> presetProfiles = new ArrayList<>(Arrays.asList(AppConstant.DEV_CODE, AppConstant.TEST_CODE, AppConstant.PROD_CODE));
        // 获取命令行中传入的配置文件和预设配置文件的交集
        presetProfiles.retainAll(profiles);
        // 返回活动配置文件
        return new ArrayList<>(profiles);
    }

    /**
     * 解析当前使用的配置文件
     *
     * @param profiles 当前的配置文件列表
     * @return 当前激活的配置文件
     */
    private static String resolveProfile(List<String> profiles) {
        // 如果没有配置环境，则默认使用开发环境
        if (profiles.isEmpty()) {
            return AppConstant.DEV_CODE;
        }
        // 如果只有一个环境配置，直接返回
        if (profiles.size() == 1) {
            return profiles.get(0);
        }
        // 如果存在多个环境配置，抛出异常
        throw new RuntimeException("同时存在多个环境变量: " + StringUtils.arrayToCommaDelimitedString(profiles.toArray()));
    }

    /**
     * 配置系统属性（例如：spring.profiles.active，blade.is-local等）
     *
     * @param profile 当前激活的配置文件
     */
    private static void configureSystemProperties(String profile) {
        Properties props = System.getProperties();
        // 配置Spring激活的环境
        props.setProperty("spring.profiles.active", profile);
        // props.setProperty("spring.main.web-application-type", "reactive");
    }

    /**
     * 初始化并返回配置后的SpringApplicationBuilder
     *
     * @param source  启动类
     * @param appName 应用名称
     * @param profile 当前的配置文件
     * @return 配置后的SpringApplicationBuilder
     */
    private static SpringApplicationBuilder initializeSpringApplicationBuilder(Class<?> source, String appName, String profile) {
        // 获取启动Jar的路径
        String startJarPath = DhccApplication.class.getResource("/").getPath().split("!")[0];
        // 输出启动信息，包含当前的环境变量和Jar路径
        System.out.println(String.format("----启动中，读取到的环境变量:[%s]，jar地址:[%s]----", profile, startJarPath));

        SpringApplicationBuilder builder = new SpringApplicationBuilder(source);
        // 设置当前激活的配置文件
        builder.profiles(profile);

        // 执行启动服务的逻辑
        List<LauncherService> launcherList = new ArrayList();
        ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
        launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
                .forEach(launcherService -> launcherService.launcher(builder, appName, profile));

        // 返回配置后的SpringApplicationBuilder
        return builder;
    }

    /**
     * 打印应用的关键启动信息
     *
     * @param context 应用上下文
     */
    private static void printApplicationInfo(ConfigurableApplicationContext context) {
        // 从上下文中获取 Environment 环境信息
        Environment environment = context.getEnvironment();

        // 提取关键信息
        String appName = environment.getProperty("spring.application.name", "Unknown Application");
        String serverPort = environment.getProperty("server.port", "Unknown Port");
        String contextPath = environment.getProperty("server.servlet.context-path", "/");
        String activeProfiles = String.join(", ", environment.getActiveProfiles());

        // 打印启动信息
        System.out.println("=========================================");
        System.out.println("应用启动成功!");
        System.out.println("应用名称: " + appName);
        System.out.println("服务器端口: " + serverPort);
        System.out.println("上下文路径: " + contextPath);
        System.out.println("激活的配置文件: " + (activeProfiles.isEmpty() ? "默认" : activeProfiles));
        System.out.println("=========================================");

    }

    /**
     * 判断是否为本地开发环境
     *
     * @return 如果是本地开发环境返回true，否则返回false
     */
    public static boolean isLocalDev() {
        String osName = System.getProperty("os.name");
        // 判断操作系统，如果是Linux系统，则认为不是本地开发环境
        return StringUtils.hasText(osName) && !AppConstant.OS_NAME_LINUX.equalsIgnoreCase(osName);
    }

    public static Map<String,String> mapStringToMap(String str){
        str=str.substring(1, str.length()-1);
        String[] strs=str.split(",");
        Map<String,String> map = new HashMap<String, String>();
        for (String string : strs) {
            String key= string.split("=")[0];
            String value = "";
            if( string.split("=").length>1){
                value = string.split("=")[1];
            }
            map.put(key, value);
        }
        return map;
    }
}
